//
// Created by edward on 23-2-17.
//

#ifndef CONCURRENCE_LIST_H
#define CONCURRENCE_LIST_H

#include <mutex>
#include <memory>

namespace edward::multithread {

    template<typename T>
    class List {
        using Ptr = std::shared_ptr<T>;
        struct Node;
        using NodePtr = std::unique_ptr<Node>;
        struct Node {
            std::mutex mtx_;
            Ptr data_;
            NodePtr next_;
            explicit Node(const Ptr &data = Ptr{}): data_(data) {}
        };
        NodePtr head_;
    public:
        List(): head_(new Node) {}
        List(const List&) = delete;
        List& operator= (const List&) = delete;

        void add(T t) {
            NodePtr newNode(new Node(std::make_shared<T>(std::move(t))));
            std::lock_guard lk(head_->mtx_);
            newNode->next_ = std::move(head_->next_);
            head_->next_ = std::move(newNode);
        }

        template<typename Function>
        void for_each(Function f)  {    //考虑到多线程环境下可能引用失效，不使用万能引用，而是简单传值
            Node *cur = head_.get(), *next;
            std::unique_lock<std::mutex> lk(head_->mtx_);
            while (next = cur->next_.get()) {
                std::unique_lock<std::mutex> nextLk(next->mtx_);
                lk.unlock();
                f(next->data_);
                cur = next;
                lk = std::move(nextLk);
            }
        }

        template<typename Predicate>
        Ptr find_first_if(Predicate p) {
            Node *cur = head_.get(), *next;
            std::unique_lock<std::mutex> lk(head_->mtx_);
            while (next = cur->next_.get()) {
                std::unique_lock<std::mutex> nextLk(next->mtx_);
                lk.unlock();
                if (p(next->data_)) {
                    return next->data_;
                }
                cur = next;
                lk = std::move(nextLk);
            }
            return Ptr{};
        }

        template<typename Predicate>
        void remove(Predicate p) {
            Node* cur = head_.get(), *next;
            std::unique_lock<std::mutex> lk(head_->mtx_);
            while (next = cur->next_.get()) {
                std::unique_lock<std::mutex> nextLk(next->mtx_);
                if (p(next->data_)) {
                    NodePtr oldNext = std::move(cur->next_);    //避免next节点的释放导致后面所有节点的释放
                    cur->next_ = std::move(next->next_);
                    nextLk.unlock();    //这个锁已经没有用了，但还是释放掉
                } else {
                    lk.unlock();        //尽可能缩小临界区
                    cur = next;
                    lk = std::move(nextLk);
                }
            }
        }

    };

}

#endif //CONCURRENCE_LIST_H
